package edu.ynu.se.xiecheng.achitectureclass.util;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.ynu.se.xiecheng.achitectureclass.entity.Device;
import edu.ynu.se.xiecheng.achitectureclass.entity.Greenhouse;
import edu.ynu.se.xiecheng.achitectureclass.entity.Statistic;
import edu.ynu.se.xiecheng.achitectureclass.service.DeviceService;
import edu.ynu.se.xiecheng.achitectureclass.service.GreenhouseService;
import edu.ynu.se.xiecheng.achitectureclass.service.StatisticService;
import lombok.Data;
import okhttp3.*;

import java.io.IOException;
import java.util.*;

public class NetworkHandler {

    private static final String F_BOX_TOKEN_URL = "https://fbox360.com/idserver/core/connect/token";
    private static final String CAMERA_TOKEN_URL = "http://42.193.14.241:7000/ysapi/subAccount/getToken";
    private static final String STATISTIC_URL = "https://fbox360.com/api/v2/dmon/value/get?boxNo=";
    private static final String CAMERA_URL = "https://open.ys7.com/api/lapp/device/capture";
    public static final Map<String, List<String>> DEVICES_ID_MAP = new HashMap<>();
    public static final String CAMERA_ID1 = "AB9831171";
    public static final String CAMERA_ID2 = "AC0174303";
    private static final MediaType JSON = MediaType.get("application/json");
    private static final PostCameraToken POST_CAMERA_TOKEN = new PostCameraToken("1ca7f0ab24ba41b291346347ec30140e");
    private static final RequestBody F_BOX_TOKEN_BODY = genFormBody("scope", "fbox", "client_id",
            "68db2e8bda8d47b5b5db7eaf71c7dcdd", "client_secret", "76dc724c95004acab25482d344dab407",
            "grant_type", "client_credentials");
    private static final OkHttpClient CLIENT = new OkHttpClient();
    private static final ObjectMapper MAPPER = new ObjectMapper();
    public static String fBoxToken = null, cameraToken = null;
    private static long tokenTime = 0, tokenExpireTime = 0, cameraTokenExpireTime = 0;

    static {
        ArrayList<String> list = new ArrayList<>(11);

        new ArrayList<>(11);
        list.add("327061586065758005");
        list.add("327061586065758015");
        list.add("327061586065758006");
        list.add("327061586065758007");
        list.add("327061586065758008");
        list.add("327061586065758009");
        list.add("327061586065758010");
        list.add("327061586065758013");
        list.add("327061586065758014");
        list.add("327061586065758012");
        list.add("327061586065758011");
        DEVICES_ID_MAP.put("300822040454", list);


        ArrayList<String> list2 = new ArrayList<>(11);

        new ArrayList<>(11);
        list2.add("327061430532576246");
        list2.add("327061430532576250");
        list2.add("327061430531527664");
        list2.add("327061430532576241");
        list2.add("327061430532576242");
        list2.add("327061430532576243");
        list2.add("327061430532576244");
        list2.add("327061430532576247");
        list2.add("327061430532576248");
        list2.add("327061430532576249");
        list2.add("327061430532576245");
        DEVICES_ID_MAP.put("300023040198", list2);





        MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    private static void tryUpdateToken() {
        if (System.currentTimeMillis() - tokenTime > tokenExpireTime) {
            try {
                String result = postForm(F_BOX_TOKEN_URL, F_BOX_TOKEN_BODY);
                if (result == null) return;
                ReceivedToken token = MAPPER.readValue(result, ReceivedToken.class);
                tokenTime = System.currentTimeMillis();
                tokenExpireTime = token.getExpires_in() * 1000;
                fBoxToken = token.getAccess_token();
                System.out.println("Update Token Success");
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }


    public static void getStatistic(StatisticService statisticService, DeviceService deviceService) {
        tryUpdateToken();
        Set<Map.Entry<String, List<String>>> entries = DEVICES_ID_MAP.entrySet();
        for (Map.Entry<String, List<String>> entry : entries) {
            String boxNo = entry.getKey();
            List<String> devicesIds = entry.getValue();

            if (fBoxToken != null) {
                try {
                    String json = MAPPER.writeValueAsString(new PostStatistic(devicesIds));
                    String result = postJson(STATISTIC_URL + boxNo, json, "Authorization", "Bearer " + fBoxToken);
                    if (result == null) return;
                    List<ReceivedStatistic> receivedStatistics = MAPPER.readValue(result, MAPPER.getTypeFactory().constructCollectionType(List.class, ReceivedStatistic.class));
                    for (ReceivedStatistic receivedStatistic : receivedStatistics) {
                        Statistic statistic = new Statistic();
                        statistic.setStatisticName(receivedStatistic.getName());
                        statistic.setStatisticValue(receivedStatistic.getValue());
                        Device device = deviceService.getDeviceByDeviceNumber(receivedStatistic.getId());
                        device.addStatistic(statistic);
                        statisticService.POST(statistic);
                        deviceService.PUT(device);
                    }
                    System.out.println("Get Statistic Success");
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }

    }

    private static void tryUpdateCameraToken(GreenhouseService greenhouseService) {
        List<Greenhouse> all = greenhouseService.getAll();
        for (Greenhouse greenhouse : all) {
            Long time = greenhouse.getCameraTokenExpireTime();
            if (time != null) {
                cameraTokenExpireTime = Math.max(cameraTokenExpireTime, greenhouse.getCameraTokenExpireTime());
                cameraToken = greenhouse.getCameraToken();
            }
        }
        if (System.currentTimeMillis() > cameraTokenExpireTime) {
            try {
                String json = MAPPER.writeValueAsString(POST_CAMERA_TOKEN);
                String result = postJson(CAMERA_TOKEN_URL, json);
                if (result == null) return;

                ReceivedCameraToken token = MAPPER.readValue(result, ReceivedCameraToken.class);
                cameraTokenExpireTime = token.getData().expireTime;
                cameraToken = token.getData().accessToken;
                all.forEach(greenhouse -> {
                    greenhouse.setCameraTokenExpireTime(cameraTokenExpireTime);
                    greenhouse.setCameraToken(cameraToken);
                    greenhouseService.PUT(greenhouse);
                });
                System.out.println("Update Camera Token Success");
                System.out.println("Camera Token: " + cameraToken);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void getCameraData(GreenhouseService greenhouseService, StatisticService statisticService, DeviceService deviceService) {
        getCameraData(greenhouseService,statisticService,deviceService,CAMERA_ID1);
        getCameraData(greenhouseService,statisticService,deviceService,CAMERA_ID2);
    }

    public static void getCameraData(GreenhouseService greenhouseService, StatisticService statisticService, DeviceService deviceService,String cameraId) {
        tryUpdateCameraToken(greenhouseService);
        if (cameraToken != null) {
            try {
                String result = postForm(CAMERA_URL, genFormBody("accessToken", cameraToken, "deviceSerial", cameraId,
                        "channelNo", "1", "quality", "0"));
                if (result == null) return;
                ReceivedCameraData receivedCameraData = MAPPER.readValue(result, ReceivedCameraData.class);
                if (receivedCameraData.code.equals("10028")) return;
                Device device = deviceService.GET(6L);
                Statistic statistic = new Statistic();
                statistic.setStatisticName("萤石摄像头");
                statistic.setStatisticPhoto(ImageUtil.getImageBytes(receivedCameraData.getData().getPicUrl()));
                device.addStatistic(statistic);
                statisticService.POST(statistic);
                deviceService.PUT(device);
                System.out.println("Get Camera Data Success");
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private static RequestBody genFormBody(String... content) {
        FormBody.Builder builder = new FormBody.Builder();
        for (int i = 0; i < content.length - 1; i += 2) {
            builder.add(content[i], content[i + 1]);
        }
        return builder.build();
    }

    private static String postForm(String url, RequestBody formBody, String... headers) throws IOException {
        Map<String, String> headersMap = new HashMap<>();
        for (int i = 0; i < headers.length - 1; i += 2) {
            headersMap.put(headers[i], headers[i + 1]);
        }
        headersMap.put("Content-Type", "application/x-www-form-urlencoded");
        return post(url, headersMap, formBody);
    }

    private static String postJson(String url, String json, String... headers) throws IOException {
        RequestBody body = RequestBody.create(json, JSON);
        Map<String, String> headersMap = new HashMap<>();
        for (int i = 0; i < headers.length - 1; i += 2) {
            headersMap.put(headers[i], headers[i + 1]);
        }
        headersMap.put("Content-Type", "application/json");
        return post(url, headersMap, body);
    }

    private static String post(String url, Map<String, String> headers, RequestBody body) throws IOException {
        Request request = new Request.Builder()
                .url(url)
                .headers(Headers.of(headers))
                .post(body)
                .build();
        try (Response response = CLIENT.newCall(request).execute()) {
            if (response.body() != null) {
                return response.body().string();
            }
        }
        return null;
    }

    @Data
    private static class ReceivedToken {
        private String access_token;
        private Long expires_in;
        private String token_type;
        private String scope;
    }

    @Data
    private static class ReceivedStatistic {
        private String id;
        private String name;
        private Long value;
    }

    @Data
    private static class PostStatistic {
        private List<String> ids;

        public PostStatistic(List<String> ids) {
            this.ids = ids;
        }
    }

    @Data
    private static class ReceivedCameraToken {
        private String result;
        private String page;
        private CameraTokenData data;
        private String code;
        private String msg;
        private String meta;
    }

    @Data
    private static class CameraTokenData {
        private String accessToken;
        private Long expireTime;
    }

    @Data
    private static class PostCameraToken {
        private String accountId;

        public PostCameraToken(String accountId) {
            this.accountId = accountId;
        }
    }

    @Data
    private static class ReceivedCameraData {
        private String msg;
        private String code;
        private CameraData data;
    }

    @Data
    private static class CameraData {
        private String picUrl;
    }

}
